#pragma once

#include <assert.h>
#include <boost/iostreams/filter/symmetric.hpp>
#include <alpha/utility/convert.hpp>

namespace alpha { namespace crypto {

namespace detail {

struct hex_encoder_impl
{
	typedef char char_type;
	bool filter(char const *&begin_in, char const *end_in, char *&begin_out, char *end_out, bool flush)
	{
		for (; begin_in != end_in && begin_out != end_out; ++begin_in)
			begin_out = alpha::utility::ord2hex(*begin_in, begin_out);
		return false;
	}
	void close() {}
};

struct hex_decoder_impl
{
	typedef char char_type;
	hex_decoder_impl(): ch_(-1) {}
	bool filter(char const *&begin_in, char const *end_in, char *&begin_out, char *end_out, bool flush)
	{
		if (ch_ != -1)
		{
			if (end_in == begin_in)
				return false;
			ch_ |= alpha::utility::hex2ord(*begin_in++);
			*begin_out++ = ch_;
			ch_ = -1;
		}
		for (; end_in != begin_in && begin_out != end_out; ++begin_out)
		{
			int r = alpha::utility::hex2ord(*begin_in++) << 4;
			if (begin_in == end_in)
			{
				assert(ch_ == -1);
				ch_ = r;
				break;
			}
			r |= alpha::utility::hex2ord(*begin_in++);
			*begin_out = r;
		}
		return false;
	}
	void close() {}
private:
	int ch_;
};

} // namespace detail
	
template <typename Alloc = std::allocator<char> >
struct basic_hex_encoder: boost::iostreams::symmetric_filter<detail::hex_encoder_impl, Alloc>
{
	basic_hex_encoder(int buffer_size = boost::iostreams::default_device_buffer_size)
		: boost::iostreams::symmetric_filter<detail::hex_encoder_impl, Alloc>(buffer_size)
	{}
};
BOOST_IOSTREAMS_PIPABLE(basic_hex_encoder, 1)

template <typename Alloc = std::allocator<char> >
struct basic_hex_decoder: boost::iostreams::symmetric_filter<detail::hex_decoder_impl, Alloc>
{
	basic_hex_decoder(int buffer_size = boost::iostreams::default_device_buffer_size)
	: boost::iostreams::symmetric_filter<detail::hex_decoder_impl, Alloc>(buffer_size)
	{}
};
BOOST_IOSTREAMS_PIPABLE(basic_hex_decoder, 1)

typedef basic_hex_encoder<> hex_encoder;
typedef basic_hex_decoder<> hex_decoder;

}} // namespace alpha::crypto
